

/*
	Rev 2007.03.03
	For the latest version of this template, http://quiziqal.com/files/default.aspx		
*/

string ObjectQualifier = "";
private string strippedTablePrefixes = "";

public string GetRawStoredProcName(string moduleName, string keyword)
{
	return moduleName + keyword;
}

public string GetRawStoredProcName(string moduleName, string keyword, string columnName)
{
	return moduleName + keyword + "By" + columnName;
}

public string GetStoredProcName(string moduleName, string keyword)
{
	return GetTableOwner() + GetObjectQualifier() + moduleName + keyword;
}

public string GetMethodName(string methodName, string keyword)
{
	return	keyword + methodName;
}

public string GetMethodName(string moduleName, string keyword, bool makePlural)
{
	string methodName = "";
	
	if(makePlural)
	{
		String m = Pluralizer.ToPlural(moduleName);
		methodName = GetMethodName(m, keyword);
	}
	else
		methodName = GetMethodName(moduleName, keyword);
		
	return methodName;
}

public string GetMethodName(string methodName, string keyword, string fieldName)
{
	return keyword + methodName + "By" + fieldName;
}

public string GetTableOwner()
{
	return "{databaseOwner}";
}

public string GetObjectQualifier()
{
	return "{objectQualifier}" + ObjectQualifier;
}

public string GetTableName(TableSchema table)
{
	string tableName = table.Name;
	
	if( ObjectQualifier.Length > 0 )
	{
		return tableName.Replace(ObjectQualifier, "");
	}
	else
	{
	
		string[] strips = StrippedTablePrefixes.Split(new char[] {',', ';'});		
		foreach(string strip in strips)
		{
			
			if (tableName.StartsWith(strip))
			{
				tableName = tableName.Remove(0, strip.Length);
				continue;
			}
		}
		
		return tableName;
	}
}

public string GetModuleNameFromTable(TableSchema table, String strippedModulePrefixes)
{
	string tableName = GetTableName(table);

	// Make Singular
	if (tableName.EndsWith("oes"))
	{
		tableName = tableName.Substring(0, tableName.Length-2);
	}
	else if (tableName.EndsWith("s"))
	{
		tableName = tableName.Substring(0, tableName.Length-1);
	}

	tableName = GetModuleNameFromTable(tableName, strippedModulePrefixes);
	
	return tableName;
}

public string GetModuleNameFromTable(string tableName, String strippedModulePrefixes)
{
	
	string[] strips = strippedModulePrefixes.Split(new char[] {',', ';'});		
	foreach(string strip in strips)
	{
		String trimmedStrip = strip.Trim();
		if (tableName.StartsWith(trimmedStrip))
		{
			tableName = tableName.Remove(0, trimmedStrip.Length);
			break;
		}
	}
	
	
	
	String[] tokens = tableName.Replace('_', ' ').Split(' ');
		
	for (int i=0 ;i < tokens.Length; i++)
	{
		String s = tokens[i];

		// convert all UPPERCASE tokens into TitleCase
		if (s == s.ToUpper() && s.Length > 1)
		{
			s = s[0] + s.Substring(1).ToLower();
			tokens[i] = s;
		}
	}
	System.String joined = System.String.Join(" ", tokens );
		
	tableName = GetIdentifierFromString(joined);
	
	return tableName;
}




public string GetPrimaryKeyParameters(TableSchema table, bool includeTypes, string vbOrCsharp)
{
	string parameters = "";
	
	

	if (table.PrimaryKey != null)
	{
		for( int i = 0; i < table.PrimaryKey.MemberColumns.Count; i++ )
		{
			if( includeTypes && (vbOrCsharp == "cs"))  
			{
				parameters = parameters + GetCSharpVariableType(table.PrimaryKey.MemberColumns[i]) + " ";				          
			}
			else if( includeTypes && (vbOrCsharp == "vb"))
			{
				parameters = parameters + "ByVal ";
			}

			parameters = parameters + GetLocalVariableName(table.PrimaryKey.MemberColumns[i]);

       		if( includeTypes && (vbOrCsharp == "vb")) 
			{
				parameters = parameters + " As " + GetVBVariableType(table.PrimaryKey.MemberColumns[i]) + " ";
			}
          
      		parameters = parameters + ", ";
		}
	}
	else
	{
		throw new ApplicationException("This template will only work on tables with a primary key.");
	}
	return parameters.Remove(parameters.Length - 2, 2);
}

public string GetModuleIdParameter(TableSchema table, bool includeType, bool prependComma, string vbOrCSharp)
{	
	string moduleIdParam = "";
	
	if(table.Columns.Contains("moduleId"))
	{
		if( includeType )
		{
			moduleIdParam = vbOrCSharp == "vb" ? "ByVal moduleId As Integer" : "int moduleId";
		}
		else
		{
			moduleIdParam = "moduleId";
		}
		
		if(prependComma)
		{
			moduleIdParam = ", " + moduleIdParam;
		}
	}
	
	return moduleIdParam;
}

public string GetUserIdParameter(TableSchema table, bool includeType, bool prependComma, string vbOrCSharp)
{	
	string userIdParam = "";
	
	if(table.Columns.Contains("UserId"))
	{
		if( includeType )
		{
			userIdParam = vbOrCSharp == "vb" ? "ByVal userId As Long" : "long userId";
		}
		else
		{
			userIdParam = "userId";
		}
		
		if(prependComma)
		{
			userIdParam = ", " + userIdParam;
		}
	}
	
	return userIdParam;
}

public string GetUserIdAndModuleIdParameters(TableSchema table, bool includeType, bool prependComma, string vbOrCSharp)
{
	String s = this.GetModuleIdParameter(table, includeType, prependComma, vbOrCSharp).Trim();
	
	if (s.Length == 0 || prependComma)
	{
		s += this.GetUserIdParameter(table, includeType, prependComma, vbOrCSharp);
	}
	else
	{
		s += this.GetUserIdParameter(table, includeType, true, vbOrCSharp);
	}
	return s;
}

/*
public string GetFKMemberColumns(TableKeySchema fk)
{
	string s="";
	for( int j = 0; j < fk.ForeignKeyMemberColumns.Count; j++)
	{
		s += GetSqlParameterStatement(fk.ForeignKeyMemberColumns[j]);
		if (j < fk.ForeignKeyMemberColumns.Count - 1) s += ", \n\t";
	}
	return s;
}
*/

public string GetFKMemberColumnsWhere(TableKeySchema fk)
{
	string s="";
	for( int j = 0; j < fk.ForeignKeyMemberColumns.Count; j++)
	{
		s += "["+fk.ForeignKeyMemberColumns[j].Name + "]=@" + fk.ForeignKeyMemberColumns[j].Name ;
		if (j < fk.ForeignKeyMemberColumns.Count - 1) s += " AND ";
	}
	return s;
}

public string GetMemberVariableDeclarationStatement(ColumnSchema column, LanguageOutput languageOutput)
{
	return GetMemberVariableDeclarationStatement("private", column, languageOutput);
}

public string GetMemberVariableDeclarationStatement(string protectionLevel, ColumnSchema column, LanguageOutput languageOutput)
{
	string statement = protectionLevel + " ";
  
  if (languageOutput == LanguageOutput.CS)
    statement = statement + GetCSharpVariableType(column) + " ";
  
  statement = statement + GetMemberVariableName(column);
  
  if (languageOutput == LanguageOutput.VB)
  statement = statement + " as " + GetVBVariableType(column);
	
	string defaultValue = GetMemberVariableDefaultValue(column);
	if (defaultValue != "")
	{
		statement += " = " + defaultValue;
	}
  
  if (languageOutput == LanguageOutput.CS)
    statement = statement + ";";
	
	return statement;
}

public string GetReaderAssignmentStatement(ColumnSchema column, int index)
{
	string statement = "if (!reader.IsDBNull(" + index.ToString() + ")) ";
	statement += GetMemberVariableName(column) + " = ";
	
	if (column.Name.EndsWith("TypeCode")) statement += "(" + column.Name + ")";
	
	statement += "reader." + GetReaderMethod(column) + "(" + index.ToString() + ");";
	
	return statement;
}

public string GetCamelCaseName(string value)
{
	return value.Substring(0, 1).ToLower() + value.Substring(1);
}

public string GetPascalCaseName(string value)
{
	return value.Substring(0, 1).ToUpper() + value.Substring(1);
}


public string GetMemberVariableName(ColumnSchema column)
{
	string propertyName = GetPropertyName(column);
	string memberVariableName = "m" + GetPascalCaseName(propertyName);
	
	return memberVariableName;
}

public string GetLocalVariableName(ColumnSchema column)
{
	string propertyName = GetPropertyName(column);
	string localVariableName = GetCamelCaseName(propertyName);
	
	return localVariableName;
}


public string GetPropertyName(ColumnSchema column)
{
	string propertyName = GetIdentifierForColumn(column);
	
	//if (propertyName == column.Table.Name + "Name") return "Name";
	//if (propertyName == column.Table.Name + "Description") return "Description";
	
	if (propertyName.EndsWith("TypeCode")) propertyName = propertyName.Substring(0, propertyName.Length - 4);
	
	return propertyName;
}

public String GetIdentifierForColumn(ColumnSchema column)
{
	string propertyName = GetIdentifierFromString(column.Name);
	
	return propertyName;
	
}

public String GetIdentifierFromString(String propertyName)
{

	System.Globalization.CultureInfo cultureInfo = System.Threading.Thread.CurrentThread.CurrentCulture;
    //Create TextInfo object.
    System.Globalization.TextInfo textInfo = cultureInfo.TextInfo;	


	// replace all _ to space
	propertyName= propertyName.Replace("_", " ");
	

		
	if (propertyName.IndexOf(' ') >= 0)
	{
		// convert to TitleCase
		propertyName= textInfo.ToTitleCase(propertyName);
		
		// remove all spaces
		propertyName= propertyName.Replace(" ", "");
	}
		
	return propertyName;
	
}
		
public string GetMemberVariableDefaultValue(ColumnSchema column)
{
	switch (column.DataType)
	{
		case DbType.Guid:
		{
			return "Guid.Empty";
		}
		case DbType.AnsiString:
		case DbType.AnsiStringFixedLength:
		case DbType.String:
		case DbType.StringFixedLength:
		{
			return "string.Empty";
		}
		default:
		{
			return "";
		}
	}
}

public string GetCSharpVariableType(ColumnSchema column)
{
	if (column.Name.EndsWith("TypeCode")) 
	{	
		return column.Name;
	}
	
	String s = "";
	bool chkForNullable = false;
	
	switch (column.DataType)
	{
		case DbType.AnsiString: 
			return "string";
		case DbType.AnsiStringFixedLength:
			s = "string";
			break;
		case DbType.Binary:
			s = "byte()";
			break;
		case DbType.Boolean:
			s = "bool";
			chkForNullable = true;
			break;
		case DbType.Byte:
			s = "byte";
			chkForNullable = true;
			break;
		case DbType.Currency:
		case DbType.Decimal:
		case DbType.VarNumeric:
			s = "decimal";
			chkForNullable = true;
			break;
		case DbType.Date:
		case DbType.DateTime:
			s = "DateTime";
			chkForNullable = true;
			break;
		case DbType.Double:
			s = "double";
			break;
		case DbType.Guid:
			s = "Guid";
			break;
		case DbType.Int16:
			s = "short";
			chkForNullable = true;
			break;
		case DbType.Int32:
			s = "int";
			chkForNullable = true;
			break;
		case DbType.Int64:
			s = "long";
			chkForNullable = true;
			break;
		case DbType.Object:
			s = "object";
			break;
		case DbType.SByte:
			s = "sbyte";
			chkForNullable = true;
			break;
		case DbType.Single:
			s = "float";
			break;
		case DbType.String:
		case DbType.StringFixedLength:
			s = "string";
			break;
		case DbType.Time:
			s = "TimeSpan";
			break;
		case DbType.UInt16:
			s = "ushort";
			chkForNullable = true;
			break;
		case DbType.UInt32:
			s = "uint";
			chkForNullable = true;
			break;
		case DbType.UInt64:
			s = "ulong";
			chkForNullable = true;
			break;

		default:
		{
			return "__UNKNOWN__" + column.NativeType;
		}
	}
	
	if (chkForNullable && column.AllowDBNull)
	{
		s += "?";
	}
	
	return s;

}

public string GetVBVariableType(ColumnSchema column)
{
	if (column.Name.EndsWith("TypeCode")) return column.Name;
	
	switch (column.DataType)
	{
		case DbType.AnsiString: return "string";
		case DbType.AnsiStringFixedLength: return "string";
		case DbType.Binary: return "byte()";
		case DbType.Boolean: return "Boolean";
		case DbType.Byte: return "byte";
		case DbType.Currency: return "decimal";
		case DbType.Date: return "DateTime";
		case DbType.DateTime: return "DateTime";
		case DbType.Decimal: return "decimal";
		case DbType.Double: return "double";
		case DbType.Guid: return "Guid";
		case DbType.Int16: return "short";
		case DbType.Int32: return "Integer";
		case DbType.Int64: return "long";
		case DbType.Object: return "object";
		case DbType.SByte: return "sbyte";
		case DbType.Single: return "float";
		case DbType.String: return "String";
		case DbType.StringFixedLength: return "String";
		case DbType.Time: return "TimeSpan";
		case DbType.UInt16: return "ushort";
		case DbType.UInt32: return "uint";
		case DbType.UInt64: return "ulong";
		case DbType.VarNumeric: return "decimal";
		default:
		{
			return "__UNKNOWN__" + column.NativeType;
		}
	}
}

public string GetReaderMethod(ColumnSchema column)
{
	switch (column.DataType)
	{
		case DbType.Byte:
		{
			return "GetByte";
		}
		case DbType.Int16:
		{
			return "GetInt16";
		}
		case DbType.Int32:
		{
			return "GetInt32";
		}
		case DbType.Int64:
		{
			return "GetInt64";
		}
		case DbType.AnsiStringFixedLength:
		case DbType.AnsiString:
		case DbType.String:
		case DbType.StringFixedLength:
		{
			return "GetString";
		}
		case DbType.Boolean:
		{
			return "GetBoolean";
		}
		case DbType.Guid:
		{
			return "GetGuid";
		}
		case DbType.Currency:
		case DbType.Decimal:
		{
			return "GetDecimal";
		}
		case DbType.DateTime:
		case DbType.Date:
		{
			return "GetDateTime";
		}
		default:
		{
			return "__SQL__" + column.DataType;
		}
	}
}

public string GetSqlDbType(ColumnSchema column)
{
	switch (column.NativeType)
	{
		case "bigint": return "BigInt";
		case "binary": return "Binary";
		case "bit": return "Bit";
		case "char": return "Char";
		case "datetime": return "DateTime";
		case "decimal": return "Decimal";
		case "float": return "Float";
		case "image": return "Image";
		case "int": return "Int";
		case "money": return "Money";
		case "nchar": return "NChar";
		case "ntext": return "NText";
		case "numeric": return "Decimal";
		case "nvarchar": return AppendCharColumnSize("NVarChar", column.Size);
		case "real": return "Real";
		case "smalldatetime": return "SmallDateTime";
		case "smallint": return "SmallInt";
		case "smallmoney": return "SmallMoney";
		case "sql_variant": return "Variant";
		case "sysname": return "NChar";
		case "text": return "Text";
		case "timestamp": return "Timestamp";
		case "tinyint": return "TinyInt";
		case "uniqueidentifier": return "UniqueIdentifier";
		case "varbinary": return "VarBinary";
		case "varchar": return AppendCharColumnSize("VarChar", column.Size);
		default: return "__UNKNOWN__" + column.NativeType;
	}
}

private String AppendCharColumnSize(String varchar, int s)
{
	if (s >=0)
	{
		varchar += "(" + s + ")";
	}
	else
	{
		varchar += "(MAX)";
	}
	
	return varchar;	
}


public string GetPrimaryKeyType(TableSchema table)
{
	if (table.PrimaryKey != null)
	{
		if (table.PrimaryKey.MemberColumns.Count == 1)
		{
			return GetCSharpVariableType(table.PrimaryKey.MemberColumns[0]);
		}
		else
		{
			throw new ApplicationException("This template will not work on primary keys with more than one member column.");
		}
	}
	else
	{
		throw new ApplicationException("This template will only work on tables with a primary key.");
	}
}

public string GetPrimaryKeyName(TableSchema table)
{
	if (table.PrimaryKey != null)
	{
		if (table.PrimaryKey.MemberColumns.Count == 1)
		{
			return table.PrimaryKey.MemberColumns[0].Name;
		}
		else
		{
			throw new ApplicationException("This template will not work on primary keys with more than one member column.");
		}		
	}
	else
	{
		throw new ApplicationException("This template will only work on tables with a primary key.");
	}
}

public string GetSelectByColumnNameParameter(ColumnSchema column, bool includeTypes, bool wrapDbNullColumnsWithNullFunction, string vbOrCSharp)
{
	string parameter = "";
	bool allowDbNull = column.AllowDBNull;
	
  if( includeTypes && (vbOrCSharp == "cs"))  //MJV
	  parameter = parameter + GetCSharpVariableType(column) + " ";		
	else if( includeTypes && vbOrCSharp == "vb" )
		parameter = parameter + "ByVal ";
	
	if(allowDbNull && wrapDbNullColumnsWithNullFunction)
		parameter = parameter + string.Format("GetNull({0})", GetCamelCaseName(column.Name));
	else
		parameter = parameter + GetCamelCaseName(column.Name);
	
	if( includeTypes && vbOrCSharp == "vb" )
		parameter = parameter + " As " + GetVBVariableType(column) + " ";
		
	return parameter;
}

public string GetNonPrimaryKeyParameters(TableSchema table, bool includeTypes, bool wrapDbNullColumnsWithNullFunction, string vbOrCSharp)
{
    string parameters = "";
	if (table.PrimaryKey != null)
	{
		for( int i = 0; i < table.NonPrimaryKeyColumns.Count; i++ )
		{
			ColumnSchema column = table.NonPrimaryKeyColumns[i];
			bool allowDbNull = column.AllowDBNull;
			
			if( includeTypes && (vbOrCSharp == "cs"))  
	      	{
				parameters = parameters + GetCSharpVariableType(column) + " ";		
			}
	    	else if( includeTypes && vbOrCSharp == "vb" )
			{
				parameters = parameters + "ByVal ";
			}
				
			if(allowDbNull && wrapDbNullColumnsWithNullFunction)
			{
				parameters = parameters + string.Format("GetNull({0})", GetLocalVariableName(table.NonPrimaryKeyColumns[i]));
			}
			else
			{
				parameters = parameters + GetLocalVariableName(table.NonPrimaryKeyColumns[i]);
			}
				
			if( includeTypes && vbOrCSharp == "vb" )
				parameters = parameters + " As " + GetVBVariableType(table.NonPrimaryKeyColumns[i]) + " ";
          
      parameters = parameters + ", ";
		}
	}
	else
	{
		throw new ApplicationException("This template will only work on tables with a primary key.");
	}
  
	return parameters.Remove(parameters.Length - 2, 2);
}

public string GetClassName(TableSchema table)
{
	if( ObjectQualifier.Length > 0 )
	{
		return (table.Name + "Controller").Replace(ObjectQualifier, "");
	}
	else
	{
		return (table.Name + "Controller");
	}
}

public string GetClassNameInfo(TableSchema table)
{
	if( ObjectQualifier.Length > 0 )
	{
		return (table.Name + "Info").Replace(ObjectQualifier, "");
	}
	else
	{
		return (table.Name + "Info");
	}
}

public string GetPrimaryKeyParametersForObject(TableSchema table, string obj)
{
    string parameters = "";
	if (table.PrimaryKey != null)
	{
		for( int i = 0; i < table.PrimaryKey.MemberColumns.Count; i++ )
		{
			if(parameters.Length == 0)
			{
				parameters = parameters + obj + "." + GetPropertyName(table.PrimaryKey.MemberColumns[i]);
			}
			else
			{
				parameters = parameters + ", ";
				parameters = parameters + obj + "." + GetPropertyName(table.PrimaryKey.MemberColumns[i]);
			}
		}
	}
	else
	{
		throw new ApplicationException("This template will only work on tables with a primary key.");
	}
  
	return parameters;
}

public string GetNonPrimaryKeyParametersForObject(TableSchema table, string obj)
{
    string parameters = "";

	for( int i = 0; i < table.NonPrimaryKeyColumns.Count; i++ )
	{
		if(parameters.Length == 0)
		{
			parameters = parameters + obj + "." + GetPropertyName(table.NonPrimaryKeyColumns[i]);
		}
		else
		{
			parameters = parameters + ", ";
			parameters = parameters + obj + "." + GetPropertyName(table.NonPrimaryKeyColumns[i]);
		}
	}
	
	return parameters;
}

public static string CamelCaseToTitleCaseSentence(string inputText)
{
	// Converts CamelCase To Sentence
	// ISSUE: 'GPSDisable' would be converted as 'G PS Disable' instead of 'GPS Disable'
    String s = System.Text.RegularExpressions.Regex.Replace(inputText, "(?!^)([A-Z][a-z]+)", " $1");
    s = System.Text.RegularExpressions.Regex.Replace(s, "(?!^)([A-Z][A-Z]+)", " $1");
    return s;
}


[Category("DataSource")]
[Description("The table prefixes to strip from the classes name, delimited by comma.")]
public string StrippedTablePrefixes
{
	get {return this.strippedTablePrefixes;}
	set	{this.strippedTablePrefixes = value;}
}

public string GetBOParameter(string moduleName, bool includeType, string vbOrCSharp)
{
  string parameter = "";

  if( includeType && vbOrCSharp == "cs")
	  parameter = moduleName + "Info ";
	else if( includeType && vbOrCSharp == "vb")
		parameter = "ByVal ";
    
  parameter = parameter + "obj" + moduleName;
  
  if( includeType && vbOrCSharp == "vb")
		parameter = parameter + "As " + moduleName + "Info";
	
	return parameter;
}


public int GetMaxLength(ColumnSchema column)
{
	int v = -1;
	switch (column.NativeType)
	{
		case "char": 
		case "nchar": 
		case "nvarchar": 
		case "varchar": 
			if (column.Size > 0)
			{
				v = column.Size;
			}
		break;
	}
	
	
	return v;
}

public static bool IsIntegerField(ColumnSchema column)
{
	bool isIntField = false;
	
	switch (column.DataType)
	{
		case DbType.Byte:
		case DbType.Int16:
		case DbType.Int32:
		case DbType.Int64:
		case DbType.SByte:
		case DbType.UInt16:
		case DbType.UInt32:
		case DbType.UInt64:
			isIntField = true;
			break;
	}
	
	
	return isIntField;
}


public int GetMaxLengthForIntegerField(ColumnSchema column)
{
	int v = -1;
	
	switch (column.DataType)
	{
		case DbType.Byte:
			v = Byte.MaxValue.ToString().Length;
			break;
		case DbType.Int16:
			v = Int16.MaxValue.ToString().Length;
			break;
		case DbType.Int32:
			v = Int32.MaxValue.ToString().Length;
			break;
		case DbType.Int64:
			v = Int64.MaxValue.ToString().Length;
			break;
		case DbType.SByte:
			v = SByte.MaxValue.ToString().Length;
			break;
		case DbType.UInt16:
			v = UInt16.MaxValue.ToString().Length;
			break;
		case DbType.UInt32:
			v = UInt32.MaxValue.ToString().Length;
			break;
		case DbType.UInt64:
			v = UInt64.MaxValue.ToString().Length;
			break;
	}

	return v;
}

public new String GetSqlParameterStatement(ColumnSchema column)
{
	return String.Format("@{0} {1}", GetPropertyName(column), GetSqlDbType(column));
}

public String GetColumnAliasIfRequired(ColumnSchema column)
{
	String propertyName = GetPropertyName(column);
	if (String.Compare(propertyName, column.Name) != 0)
	{
		return "AS " + propertyName;
	}
	
	return "";
}

public String GetColumnAliasOrColumnName(ColumnSchema column)
{
	String propertyName = GetPropertyName(column);
	if (String.Compare(propertyName, column.Name) != 0)
	{
		return propertyName;
	}
	
	return '[' + column.Name + ']';
}

#region Pluralizer

public static class Pluralizer {
    #region public APIs

    public static string ToPlural(string noun) {
        return AdjustCase(ToPluralInternal(noun), noun);
    }

    public static string ToSingular(string noun) {
        return AdjustCase(ToSingularInternal(noun), noun);
    }

    public static bool IsNounPluralOfNoun(string plural, string singular) {
        return String.Compare(ToSingularInternal(plural), singular, StringComparison.OrdinalIgnoreCase) == 0;
    }

    #endregion

    #region Special Words Table

    static string[] _specialWordsStringTable = new string[] {
        "agendum",          "agenda",           "",
        "albino",           "albinos",          "",
        "alga",             "algae",            "",
        "alumna",           "alumnae",          "",
        "apex",             "apices",           "apexes",
        "archipelago",      "archipelagos",     "",
        "bacterium",        "bacteria",         "",
        "beef",             "beefs",            "beeves",
        "bison",            "",                 "",
        "brother",          "brothers",         "brethren",
        "candelabrum",      "candelabra",       "",
        "carp",             "",                 "",
        "casino",           "casinos",          "",
        "child",            "children",         "",
        "chassis",          "",                 "",
        "chinese",          "",                 "",
        "clippers",         "",                 "",
        "cod",              "",                 "",
        "codex",            "codices",          "",
        "commando",         "commandos",        "",
        "corps",            "",                 "",
        "cortex",           "cortices",         "cortexes",
        "cow",              "cows",             "kine",
        "criterion",        "criteria",         "",
        "datum",            "data",             "",
        "debris",           "",                 "",
        "diabetes",         "",                 "",
        "ditto",            "dittos",           "",
        "djinn",            "",                 "",
        "dynamo",           "",                 "",
        "elk",              "",                 "",
        "embryo",           "embryos",          "",
        "ephemeris",        "ephemeris",        "ephemerides",
        "erratum",          "errata",           "",
        "extremum",         "extrema",          "",
        "fiasco",           "fiascos",          "",
        "fish",             "fishes",           "fish",
        "flounder",         "",                 "",
        "focus",            "focuses",          "foci",
        "fungus",           "fungi",            "funguses",
        "gallows",          "",                 "",
        "genie",            "genies",           "genii",
        "ghetto",           "ghettos",           "",
        "graffiti",         "",                 "",
        "headquarters",     "",                 "",
        "herpes",           "",                 "",
        "homework",         "",                 "",
        "index",            "indices",          "indexes",
        "inferno",          "infernos",         "",
        "japanese",         "",                 "",
        "jumbo",            "jumbos",            "",
        "latex",            "latices",          "latexes",
        "lingo",            "lingos",           "",
        "mackerel",         "",                 "",
        "macro",            "macros",           "",
        "manifesto",        "manifestos",       "",
        "measles",          "",                 "",
        "money",            "moneys",           "monies",
        "mongoose",         "mongooses",        "mongoose",
        "mumps",            "",                 "",
        "murex",            "murecis",          "",
        "mythos",           "mythos",           "mythoi",
        "news",             "",                 "",
        "octopus",          "octopuses",        "octopodes",
        "ovum",             "ova",              "",
        "ox",               "ox",               "oxen",
        "photo",            "photos",           "",
        "pincers",          "",                 "",
        "pliers",           "",                 "",
        "pro",              "pros",             "",
        "rabies",           "",                 "",
        "radius",           "radiuses",         "radii",
        "rhino",            "rhinos",           "",
        "salmon",           "",                 "",
        "scissors",         "",                 "",
        "series",           "",                 "",
        "shears",           "",                 "",
        "silex",            "silices",          "",
        "simplex",          "simplices",        "simplexes",
        "soliloquy",        "soliloquies",      "soliloquy",
        "species",          "",                 "",
        "stratum",          "strata",           "",
        "swine",            "",                 "",
        "trout",            "",                 "",
        "tuna",             "",                 "",
        "vertebra",         "vertebrae",        "",
        "vertex",           "vertices",         "vertexes",
        "vortex",           "vortices",         "vortexes",
    };

    #endregion

    #region Suffix Rules Table

    static string[] _suffixRulesStringTable = new string[] {
        "ch",       "ches",
        "sh",       "shes",
        "ss",       "sses",

        "ay",       "ays",
        "ey",       "eys",
        "iy",       "iys",
        "oy",       "oys",
        "uy",       "uys",
        "y",        "ies",

        "ao",       "aos",
        "eo",       "eos",
        "io",       "ios",
        "oo",       "oos",
        "uo",       "uos",
        "o",        "oes",

        "cis",      "ces",
        "sis",      "ses",
        "xis",      "xes",

        "louse",    "lice",
        "mouse",    "mice",

        "zoon",     "zoa",

        "man",      "men",

        "deer",     "deer",
        "fish",     "fish",
        "sheep",    "sheep",
        "itis",     "itis",
        "ois",      "ois",
        "pox",      "pox",
        "ox",       "oxes",

        "foot",     "feet",
        "goose",    "geese",
        "tooth",    "teeth",

        "alf",      "alves",
        "elf",      "elves",
        "olf",      "olves",
        "arf",      "arves",
        "leaf",     "leaves",
        "nife",     "nives",
        "life",     "lives",
        "wife",     "wives",
    };

    #endregion

    #region Implementation Details

    class Word {
        public readonly string Singular;
        public readonly string Plural;
        public readonly string Plural2;

        public Word(string singular, string plural, string plural2) {
            Singular = singular;
            Plural = plural;
            Plural2 = plural2;
        }
    }

    class SuffixRule {
        string _singularSuffix;
        string _pluralSuffix;

        public SuffixRule(string singular, string plural) {
            _singularSuffix = singular;
            _pluralSuffix = plural;
        }

        public bool TryToPlural(string word, out string plural) {
            if (word.EndsWith(_singularSuffix, StringComparison.OrdinalIgnoreCase)) {
                plural = word.Substring(0, word.Length - _singularSuffix.Length) + _pluralSuffix;
                return true;
            }
            else {
                plural = null;
                return false;
            }
        }

        public bool TryToSingular(string word, out string singular) {
            if (word.EndsWith(_pluralSuffix, StringComparison.OrdinalIgnoreCase)) {
                singular = word.Substring(0, word.Length - _pluralSuffix.Length) + _singularSuffix;
                return true;
            }
            else {
                singular = null;
                return false;
            }
        }
    }

    static System.Collections.Generic.Dictionary<string, Word> _specialSingulars;
    static System.Collections.Generic.Dictionary<string, Word> _specialPlurals;
    static System.Collections.Generic.List<SuffixRule> _suffixRules;

    static Pluralizer() {
        // populate lookup tables for special words
        _specialSingulars = new System.Collections.Generic.Dictionary<string, Word>(StringComparer.OrdinalIgnoreCase);
        _specialPlurals = new System.Collections.Generic.Dictionary<string, Word>(StringComparer.OrdinalIgnoreCase);

        for (int i = 0; i < _specialWordsStringTable.Length; i += 3) {
            string s = _specialWordsStringTable[i];
            string p = _specialWordsStringTable[i + 1];
            string p2 = _specialWordsStringTable[i + 2];

            if (string.IsNullOrEmpty(p)) {
                p = s;
            }

            Word w = new Word(s, p, p2);

            _specialSingulars.Add(s, w);
            _specialPlurals.Add(p, w);

            if (!string.IsNullOrEmpty(p2)) {
                _specialPlurals.Add(p2, w);
            }
        }

        // populate suffix rules list
        _suffixRules = new System.Collections.Generic.List<SuffixRule>();

        for (int i = 0; i < _suffixRulesStringTable.Length; i += 2) {
            string singular = _suffixRulesStringTable[i];
            string plural = _suffixRulesStringTable[i + 1];
            _suffixRules.Add(new SuffixRule(singular, plural));
        }
    }

    static string ToPluralInternal(string s) {
        if (string.IsNullOrEmpty(s)) {
            return s;
        }

        // lookup special words
        Word word;

        if (_specialSingulars.TryGetValue(s, out word)) {
            return word.Plural;
        }

        // apply suffix rules
        string plural;

        foreach (SuffixRule rule in _suffixRules) {
            if (rule.TryToPlural(s, out plural)) {
                return plural;
            }
        }

        // apply the default rule
        return s + "s";
    }

    static string ToSingularInternal(string s) {
        if (string.IsNullOrEmpty(s)) {
            return s;
        }

        // lookup special words
        Word word;

        if (_specialPlurals.TryGetValue(s, out word)) {
            return word.Singular;
        }

        // apply suffix rules
        string singular;

        foreach (SuffixRule rule in _suffixRules) {
            if (rule.TryToSingular(s, out singular)) {
                return singular;
            }
        }

        // apply the default rule
        if (s.EndsWith("s", StringComparison.OrdinalIgnoreCase)) {
            return s.Substring(0, s.Length-1);
        }

        return s;
    }

    static string AdjustCase(string s, string template) {
        if (string.IsNullOrEmpty(s)) {
            return s;
        }

        // determine the type of casing of the template string
        bool foundUpperOrLower = false;
        bool allLower = true;
        bool allUpper = true;
        bool firstUpper = false;

        for (int i = 0; i < template.Length; i++) {
            if (Char.IsUpper(template[i])) {
                if (i == 0) firstUpper = true;
                allLower = false;
                foundUpperOrLower = true;
            }
            else if (Char.IsLower(template[i])) {
                allUpper = false;
                foundUpperOrLower = true;
            }
        }

        // change the case according to template
        if (foundUpperOrLower) {
            if (allLower) {
                s = s.ToLowerInvariant();
            }
            else if (allUpper) {
                s = s.ToUpperInvariant();
            }
            else if (firstUpper) {
                if (!Char.IsUpper(s[0])) {
                    s = s.Substring(0, 1).ToUpperInvariant() + s.Substring(1);
                }
            }
        }

        return s;
    }

    #endregion
}

#endregion


public enum LanguageOutput: int
{
  VB = 0,
  CS = 1
}